// bcpp_read_seq.h
//

#if !defined(BCPP_READ_SEQ_H)
#define BCPP_READ_SEQ_H

#include <vector>
#include <string>
//#include <ctype.h>
#include <iostream>
#include "bcpp_seq_formats.h"
#include "stlhelper.h"
#include "progress_indicator.h"
#include "index_string.h"
#include "cdna_prot.h"
#include <boost/shared_array.hpp>


namespace BioCPP
{

//***************************************************************************************

//		t_process_sequence

//***************************************************************************************
struct t_collect_sequences_report_errors
{
	virtual ~t_collect_sequences_report_errors(){};
	virtual void zero_length_sequence(unsigned line) = 0;
	virtual void zero_length_accession(unsigned line) = 0;
	virtual void no_matching_seq_id(unsigned line, const std::string& regex_str) = 0;
	virtual void duplicated_seq_id(unsigned previous_index, unsigned line) = 0;
};

struct t_collect_sequences_report_errors_to_strm :
		public t_collect_sequences_report_errors
{
	void zero_length_sequence(unsigned line);
	void zero_length_accession(unsigned line);
	void no_matching_seq_id(unsigned line, const std::string& regex_str);
	void duplicated_seq_id(unsigned previous_index, unsigned line);
	t_collect_sequences_report_errors_to_strm(std::ostream& CERR_) :
			CERR(CERR_)
		 {	}
	std::ostream&					CERR;
};

struct t_collect_sequences_collect_errors :
		public t_collect_sequences_report_errors
{
	void zero_length_sequence(unsigned line);
	void zero_length_accession(unsigned line);
	void no_matching_seq_id(unsigned line, const std::string& regex_str);
	void duplicated_seq_id(unsigned previous_index, unsigned line);
	std::vector<unsigned> no_matching_seq_id_line_num;
	std::vector<unsigned> empty_sequence_line_num;
	std::vector<unsigned> empty_accession_line_num;
	std::vector<unsigned> sequence_id_duplicated_prev_index;
	std::vector<unsigned> sequence_id_duplicated_line_num;
	std::string regex_str;
	virtual void report(std::ostream& os, bool detail) const;
};


struct t_collect_sequences_from_strm;
enum eCOLLECT_SEQ_DUPL{eSEQ_DUPLICATES, eSEQ_NODUPLICATES, eSEQ_APPENDDUPLICATES};


// 
//  t_collect_sequences
// 
// 
//      stores accessions and sequences
// 
//      uses names defined by regex to look up 
//              
//          stored internally in name_indices
// 
//          if allow_duplicates is eSEQ_NODUPLICATES then, the position in name_indices
//              is the index into accessions and sequences
// 
//          if allow_duplicates is true, then the position in name_indices has to have
//              an additional dereference step via name_indices
// 
// 
class t_collect_sequences
{
	friend struct t_collect_sequences_from_strm;
	std::vector<std::string>		seq;
	std::vector<std::string>		acc;
	std::vector<unsigned>			name_indices_to_seq_indices;
	eCOLLECT_SEQ_DUPL				allow_duplicates;
	t_index_string					seq_names;
	void							print_multiple_aligned(std::ostream&					os,
															const std::vector<unsigned>&	valid_indices,
															unsigned						line_wrap,
															bool							interleaved,
															bool							names_only_first_line,
                                                            bool                            invalid_to_underscores = false) const;
	void							print_fasta(std::ostream&					os,
												const std::vector<unsigned>&	valid_indices,
												unsigned						line_len) const;
	void							print_clustal(std::ostream&					os,
												const std::vector<unsigned>&	valid_indices,
												unsigned						line_len) const;
	void							print_phylip(std::ostream&					os,
												const std::vector<unsigned>&	valid_indices,
												unsigned						line_len,
												bool							interleaved) const;

public:
	t_collect_sequences(eCOLLECT_SEQ_DUPL allow_duplicates_ = eSEQ_DUPLICATES)
						:allow_duplicates(allow_duplicates_)		{	}
	void init_from_stream(
						std::istream& istrm,
						std::string							seqid_regex_str,
						t_progress_indicator&				dots_,
						t_collect_sequences_report_errors& 	error_report_,
						const std::string&					valid_sequence_chars  =
															"ACDEFGHIKLMNPQRSTVWXYacdefghiklmnpqrstvwxy",
						bool								to_uppercase =false,
                        bool                                letters_are_valid=true);
    void set_allow_duplicates(eCOLLECT_SEQ_DUPL allow_duplicates_){allow_duplicates = allow_duplicates_;}
    eCOLLECT_SEQ_DUPL get_allow_duplicates() const {return allow_duplicates;}
	const std::deque<std::string>&	names() const		{	return seq_names.get_deque();}
    void                            replace_names(const t_index_string&	new_seq_names);
    void                            replace_accessions(const std::vector<std::string>&	new_acc)
                                                        {   assert(acc.size() ==
                                                                   new_acc.size());
                                                            acc = new_acc;}
	const std::vector<std::string>&	sequences() const	{	return seq;}
	const std::vector<std::string>&	accessions() const	{	return acc;}
	const std::string&				name(unsigned i) const
											{assert (i < name_indices_to_seq_indices.size());
												assert (name_indices_to_seq_indices[i] < seq_names.size());
												return seq_names.string_at(name_indices_to_seq_indices[i]);}
	bool							get_index_of_name(const std::string& name, unsigned& i) const;
	void							get_indices_of_name(const std::string& name,
												std::vector<unsigned>& indices) const;
	const std::string&				sequence(unsigned i) const{	assert( i< seq.size()); return seq[i];}
	std::string&					sequence(unsigned i) {	assert( i< seq.size()); return seq[i];}
	const std::string&				accession(unsigned i) const{	assert( i< acc.size()); return acc[i];}
	bool							sequence(const std::string& name, std::string& sequence) const;
	bool							sequence(const std::string& name, std::string& sequence,
												std::string& accession) const;
	void							sequence(const std::string& name, std::vector<std::string>&  seq) const;
	void							sequence(const std::string& name, std::vector<std::string>&  seq,
														std::vector<std::string>& acc) const;

	unsigned						size() const	{	return seq.size();}
	void							print_sequences(eSEQ_FORMAT 					format,
													std::ostream&					os,
													const std::vector<unsigned>&	valid_indices,
													unsigned						line_len) const;
};
}

#endif //BCPP_READ_SEQ_H
